package main

import (
	"net/http"
	"io/ioutil"
	"encoding/json"
	"strings"
	"time"
	"sync"
)

// represent a Jetbrains product
type Product struct {
	Code        string           `json:"code"`
	Name        string           `json:"name"`
	Link        string           `json:"link"`
	Description string           `json:"description"`
	Releases    []ProductRelease `json:"releases"`
}

type ProductLatest struct {
	Code          string         `json:"code"`
	Name          string         `json:"name"`
	Link          string         `json:"link"`
	Description   string         `json:"description"`
	LatestRelease ProductRelease `json:"latest_release"`
}

type ProductRelease struct {
	Date string `json:"date"`
	Type string `json:"type"`
	Downloads map[string]struct {
		Link         string `json:"link"`
		Size         int    `json:"size"`
		ChecksumLink string `json:"checksumLink"`
	} `json:"downloads"`
	Version      string `json:"version"`
	MajorVersion string `json:"majorVersion"`
	Build        string `json:"build"`
}

const ProductService = "https://data.services.jetbrains.com/products"

var productsLock sync.RWMutex
var products []Product
var lastUpdateTime time.Time

func init() {
	updateProduct()
}

func productHandler(w http.ResponseWriter, r *http.Request) {
	if products == nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	if lastUpdateTime.Add(24 * time.Hour).Before(time.Now()) {
		updateProduct()
	}
	// HTTP Query Params
	query := r.URL.Query()
	code := query.Get("code")
	name := query.Get("name")
	var latestProducts []*ProductLatest
	latestProducts = make([]*ProductLatest, 0)
	productsLock.RLock()
	defer productsLock.RUnlock()
	for index, _ := range products {
		if code != "" && code != products[index].Code {
			continue
		}
		if name != "" && !strings.Contains(products[index].Name, name) {
			continue
		}
		if len(products[index].Releases) == 0 {
			logger.Infof("no releases found for product %s (%s)", products[index].Name, products[index].Code)
			continue
		}
		latestProducts = append(latestProducts, &ProductLatest{
			Code:          products[index].Code,
			Name:          products[index].Name,
			Link:          products[index].Link,
			Description:   products[index].Description,
			LatestRelease: products[index].Releases[0],
		})
	}
	bs, err := json.Marshal(latestProducts)
	if err != nil {
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
	w.Write(bs)
}

func updateProduct() {
	logger.Infof("retrieval product info from jetbrains service ...")
	resp, err := httpClient.Get(ProductService)
	if err != nil {
		logger.Errorf("initialize product info error: %s", err.Error())
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logger.Errorf("initialize product info error: %s", err.Error())
		return
	}
	productsLock.Lock()
	defer productsLock.Unlock()
	err = json.Unmarshal(body, &products)
	if err != nil {
		logger.Errorf("initialize product info error: %s", err.Error())
		logger.Debugf("recieve product service body: %s", string(body))
		return
	}
	lastUpdateTime = time.Now()
	logger.Infof("retrieval product info ok")
}
